Appearance
React 的 PureComponent Vs Component
本节目标:理解“React 的 PureComponent Vs Component”的核心思路,并能把它用于实际开发或面试表达。
一.它们几乎完全相同,但是PureComponent通过prop和state的浅比较来实现shouldComponentUpdate,某些情况下可以用PureComponent提升性能
1.所谓==浅比较==(shallowEqual),即react源码中的一个函数,然后根据下面的方法进行是不是==PureComponent==的判断,帮我们做了本来应该我们在==shouldComponentUpdate==中做的事情
==if== ==(====this====._compositeType== ======= ==CompositeTypes.PureClass) {====== ==shouldUpdate== ===== ==!====shallowEqual====(prevProps, nextProps)== ==||== ==!== ==shallowEqual====(inst.state, nextState);========}======
而本来我们做的事情如下,这里判断了==state==有没有发生变化(prop同理),从而决定要不要重新渲染,这里的函数在一个继承了==Component==的组件中,而这里==this.state.person==是一个对象,你会发现,在这个对象的引用没有发生变化的时候是不会重新==render==的(即下面提到的第三点),所以我们可以用==shouldComponentUpdate==进行优化,这个方法如果返回==false==,表示不需要重新进行渲染,返回==true==则重新渲染,默认返回==true==
==shouldComponentUpdate====(nextProps, nextState) {====== ==return== ==(nextState.person== ==!==== ==this====.state.person);====== ==}======
2.上面提到的某些情况下可以使用==PureComponent==来提升性能,那具体是哪些情况可以,哪些情况不可以呢,实践出真知
3.如下显示的是一个==IndexPage==组件,设置了一个==state==是==isShow==,通过一个按钮点击可以改变它的值,结果是:初始化的时候输出的是==constructor==,==render==,而第一次点击按钮,会输出一次render,即重新渲染了一次,界面也会从显示==false==变成显示==true==,但是当这个组件是继承自==PureComponent==的时候,再点击的时,不会再输出==render==,即不会再重新渲染了,而当这个组件是继承自==Component==时,还是会输出==render==,还是会重新渲染,这时候就是==PureComponent==内部做了优化的体现
4.同理也适用于==string==,==number==等基本数据类型,因为基本数据类型,值改变了就算改变了
==import== ==React, { PureComponent }== ==from== =='react'====;======
==class== ==IndexPage== ==extends== ==PureComponent===={====== ==constructor====() {====== ==super====();====== ==this====.state== ===== =={====== ==isShow:== ==false====== ==};====== ==console.====log====(===='constructor'====);====== ==}====== ==changeState== ===== ==()== ===>== =={====== ==this====.====setState====({====== ==isShow:== ==true====== ==})====== ==};====== ==render====() {====== ==console.====log====(===='render'====);====== ==return== ==(====== ==<====div====>====== ==<====button onClick========={====this====.changeState}====>====点击====</====button====>====== ==<====div====>===={====this====.state.isShow.====toString====()}====</====div====>====== ==</====div====>====== ==);====== ==}========}======
5.当这个==this.state.arr==是一个数组时,且这个组件是继承自==PureComponent==时,初始化依旧是输出==constructor==和==render==,但是当点击按钮时,界面上没有变化,也没有输出==render==,证明没有渲染,但是我们可以从下面的注释中看到,每点击一次按钮,我们想要修改的==arr==的值已经改变,而这个值将去修改==this.state.arr==,但是因为在==PureComponent==中==浅比较==这个数组的引用没有变化所以没有渲染,==this.state.arr==也没有更新,因为在==this.setState()==以后,值是在==render==的时候更新的,这里涉及到==this.setState()==的知识
6.但是当这个组件是继承自==Component==的时候,初始化依旧是输出==constructor==和==render==,但是当点击按钮时,界面上出现了变化,即我们打印处理的==arr==的值输出,而且每点击一次按钮都会输出一次==render==,证明已经重新渲染,==this.state.arr==的值已经更新,所以我们能在界面上看到这个变化
==import== ==React, { PureComponent }== ==from== =='react'====;======
==class== ==IndexPage== ==extends== ==PureComponent===={====== ==constructor====() {====== ==super====();====== ==this====.state== ===== =={====== ==arr:[===='1'====]====== ==};====== ==console.====log====(===='constructor'====);====== ==}====== ==changeState== ===== ==()== ===>== =={====== ==let== =={ arr }== ===== ==this====.state;====== ==arr.====push====(===='2'====);====== ==console.====log====(arr);====== ==// ["1", "2"]====== ==// ["1", "2", "2"]====== ==// ["1", "2", "2", "2"]== ==== ==// ....====== ==this====.====setState====({====== ==arr====== ==})====== ==};====== ==render====() {====== ==console.====log====(===='render'====);====== ==return== ==(====== ==<====div====>====== ==<====button onClick========={====this====.changeState}====>====点击====</====button====>====== ==<====div====>====== =={====this====.state.arr.====map====((item)== ===>== =={====== ==return== ==item;====== ==})}====== ==</====div====>====== ==</====div====>====== ==);====== ==}========}======
7.下面的例子用==扩展运算符==产生新数组,使==this.state.arr==的引用发生了变化,所以初始化的时候输出==constructor==和==render==后,每次点击按钮都会输出==render==,界面也会变化,不管该组件是继承自==Component==还是==PureComponent==的
==import== ==React, { PureComponent }== ==from== =='react'====;======
==class== ==IndexPage== ==extends== ==PureComponent===={====== ==constructor====() {====== ==super====();====== ==this====.state== ===== =={====== ==arr:[===='1'====]====== ==};====== ==console.====log====(===='constructor'====);====== ==}====== ==changeState== ===== ==()== ===>== =={====== ==let== =={ arr }== ===== ==this====.state;====== ==this====.====setState====({====== ==arr: [====...====arr,== =='2'====]====== ==})====== ==};====== ==render====() {====== ==console.====log====(===='render'====);====== ==return== ==(====== ==<====div====>====== ==<====button onClick========={====this====.changeState}====>====点击====</====button====>====== ==<====div====>====== =={====this====.state.arr.====map====((item)== ===>== =={====== ==return== ==item;====== ==})}====== ==</====div====>====== ==</====div====>====== ==);====== ==}========}======
8.上面的情况同样适用于==对象==的情况
二.PureComponent不仅会影响本身,而且会影响子组件,所以PureComponent最佳情况是展示组件
1.我们让==IndexPage==组件里面包含一个子组件==Example==来展示==PureComponent==是如何影响子组件的
2.父组件继承==PureComponent==,子组件继承==Component==时:下面的结果初始化时输出为==constructor==,==IndexPage render==,==example render==,但是当我们点击按钮时,界面没有变化,因为这个==this.state.person==对象的引用没有改变,只是改变了它里面的属性值所以尽管子组件是继承==Component==的也没有办法渲染,因为父组件是==PureComponent==,父组件根本没有渲染,所以子组件也不会渲染
3.父组件继承==PureComponent==,子组件继承==PureComponent==时:因为渲染在父组件的时候就没有进行,相当于被拦截了,所以子组件是==PureComponent==还是==Component==根本不会影响结果,界面依旧没有变化
4.父组件继承==Component==,子组件继承==PureComponent==时:结果和我们预期的一样,即初始化是会输出==constructor==,==IndexPage render==,==example render==,但是点击的时候只会出现==IndexPage render==,因为父组件是==Component==,所以父组件会渲染,但是
当父组件把值传给子组件的时候,因为子组件是==PureComponent==,所以它会对==prop==进行浅比较,发现这个==person==对象的引用没有发生变化,所以不会重新渲染,而界面显示是由子组件显示的,所以界面也不会变化
5.父组件继承==Component==,子组件继承==Component==时:初始化是会输出==constructor==,==IndexPage render==,==example render==,当我们第一次点击按钮以后,界面发生变化,后面就不再改变,因为我们一直把它设置为sxt2,但是每点击一次都会输出==IndexPage render==,==example render==,因为每次不管父组件还是子组件都会渲染
6.所以正如下面第四条说的,如果==state==和==prop==一直变化的话,还是建议使用==Component==,并且==PureComponent==的最好作为展示组件
==//====父组件========import== ==React, { PureComponent, Component }== ==from== =='react'====;========import== ==Example== ==from== =="../components/Example"====;======
==class== ==IndexPage== ==extends== ==PureComponent===={====== ==constructor====() {====== ==super====();====== ==this====.state== ===== =={====== ==person: {====== ==name:== =='sxt'====== ==}====== ==};====== ==console.====log====(===='constructor'====);====== ==}====== ==changeState== ===== ==()== ===>== =={====== ==let== =={ person }== ===== ==this====.state;====== ==person.name== ===== =='sxt2'====;====== ==this====.====setState====({====== ==person====== ==})====== ==};====== ==render====() {====== ==console.====log====(===='IndexPage render'====);====== ==const== =={ person }== ===== ==this====.state;====== ==return== ==(====== ==<====div====>====== ==<====button onClick========={====this====.changeState}====>====点击====</====button====>====== ==<====Example person========={person}== ==/>====== ==</====div====>====== ==);====== ==}========}========//====子组件========import== ==React, { Component }== ==from== =='react'====;======
==class== ==Example== ==extends== ==Component== =={======
==render====() {====== ==console.====log====(===='example render'====);====== ==const== =={ person }== ===== ==this====.props;====== ==return====(====== ==<====div====>====== =={person.name}====== ==</====div====>====== ==);====== ==}========}======
三.若是数组和对象等引用类型,则要引用不同,才会渲染
四.如果prop和state每次都会变,那么PureComponent的效率还不如Component,因为你知道的,进行浅比较也是需要时间
五.若有shouldComponentUpdate,则执行它,若没有这个方法会判断是不是PureComponent,若是,进行浅比较
1.继承自==Component==的组件,若是==shouldComponentUpdate==返回==false==,就不会渲染了,继承自==PureComponent==的组件不用我们手动去判断==prop==和==state==,所以在==PureComponent==中使用==shouldComponentUpdate==会有如下警告:
IndexPage has a method called shouldComponentUpdate(). shouldComponentUpdate should not be used when extending React.PureComponent. Please extend React.Component if shouldComponentUpdate is used.
也是比较好理解的,就是不要在==PureComponent==中使用==shouldComponentUpdate==,因为根本没有必要> 来自
<https://www.jianshu.com/p/c41bbbc20e65>